<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Steering Behaviours: WANDER AND PERSUE</title>

    <script src="../libs/dat.gui.min.js"></script>
    <script src="../libs/stats.min.js"></script>
    <script src="../libs/three.min.js"></script>
    <script src="../js/threejs/controls/OrbitControls.js"></script>
    <script src="../js/threejs/controls/SimplexNoise.js"></script>
    <script src="../js/ThreeSteer.js"></script>
    <script src="../js/HeightMap.js"></script>
    <script src="../js/utils.js"></script>
    <script src="../js/threex.terrain.js"></script>
    <link rel="stylesheet" type="text/css" href="../examples/css/main.css" />
    <link rel="stylesheet" type="text/css" href="../examples/css/gui.css" />
  </head>
  <script>
    let animationFrame;
    let container;
    let camera;
    let renderer;
    let controls;
    let stats;
    let scene;

    let floor;
    let terrain;

    let heightMap;
    let boundaries;

    let wanderEntities;
    let pursueEntities;

    let params;
    let iterationsCount;

    const MAP_WIDTH = 500;
    const MAP_DEPTH = 500;
    const MAP_LIMIT = 250;

    const NUM_ENTITIES = 10;
    const MAX_ITERATIONS = 20000;

    const MIN_HEIGHT_MAP_ITERATOR = -0.025;
    const MAX_HEIGHT_MAP_ITERATOR = 0.05;

    function constructTerrain() {
      if (!!scene.getObjectByName("terrain")) {
        terrain.geometry.dispose();
        terrain.material.dispose();
        scene.remove(terrain);
      }

      let terrainGeometry = THREEx.Terrain.heightMapToPlaneGeometry(
        heightMap.map
      );
      THREEx.Terrain.heightMapToVertexColor(heightMap.map, terrainGeometry);

      let terrainMaterial = new THREE.MeshBasicMaterial({
        shading: THREE.FlatShading,
        vertexColors: THREE.VertexColors,
      });

      terrain = new THREE.Mesh(terrainGeometry, terrainMaterial);

      terrain.name = "terrain";
      terrain.position.y = 50;
      terrain.rotation.x = -Math.PI * 0.5;
      terrain.scale.x = 2;
      terrain.scale.y = 2;
      terrain.scale.z = 0.2;
      terrain.scale.multiplyScalar(250);

      scene.add(terrain);
    }

    function init(element) {
      iterationsCount = 0;

      container = document.getElementById(element);
      camera = new THREE.PerspectiveCamera(
        4,
        window.innerWidth / window.innerHeight,
        2,
        50000
      );
      scene = new THREE.Scene();

      renderer = new THREE.WebGLRenderer({ alpha: true });
      renderer.setClearColor(0x333333, 0);
      renderer.setSize(window.innerWidth, window.innerHeight);
      renderer.domElement.style.position = "absolute";
      renderer.domElement.style.top = 0;
      container.appendChild(renderer.domElement);

      camera.position.set(3600, 3600, 5500);
      camera.lookAt(new THREE.Vector3(0, 0, 0));

      controls = new THREE.OrbitControls(camera, renderer.domElement);
      controls.maxPolarAngle = Math.PI * 0.5;
      controls.minDistance = 100;
      controls.maxDistance = 30000;

      stats = new Stats();
      stats.domElement.style.position = "absolute";
      stats.domElement.style.bottom = "0px";
      stats.domElement.style.left = "180px";
      stats.domElement.style.zIndex = 100;
      container.appendChild(stats.domElement);

      // Height Map
      heightMap = new HeightMap(MAP_WIDTH, MAP_DEPTH);
      heightMap.constructMap();
      heightMap.heightIterator = 0.05;

      // Floor
      let floorGeometry = new THREE.PlaneGeometry(
        MAP_WIDTH,
        MAP_DEPTH,
        MAP_WIDTH / 2,
        MAP_DEPTH / 2
      );
      let floorMaterial = new THREE.MeshBasicMaterial({
        color: 0xd3d3d3,
        transparent: true,
        opacity: 0.5,
      });
      floor = new THREE.Mesh(floorGeometry, floorMaterial);
      floor.rotation.x = -Math.PI * 0.5;
      scene.add(floor);

      // Wander entity Mesh
      let wanderEntitiesGeometry = new THREE.BoxGeometry(25, 50, 15);
      let wanderEntitiesMaterial = new THREE.MeshBasicMaterial({
        color: 0xffffff,
        wireframe: true,
      });

      let firstWanderEntityMaterial = new THREE.MeshBasicMaterial({
        color: 0xff00ff,
        wireframe: true,
      });
      // Wander entities
      wanderEntities = [];
      for (let i = 0; i < NUM_ENTITIES; i++) {
        let entityMesh;

        if (i === 0) {
          entityMesh = new THREE.Mesh(
            wanderEntitiesGeometry,
            firstWanderEntityMaterial
          );
        } else {
          entityMesh = new THREE.Mesh(
            wanderEntitiesGeometry,
            wanderEntitiesMaterial
          );
        }
        entityMesh.position.setY(25);

        let entity = new SteeringEntity(i, entityMesh);
        entity.name = "wanderEntity" + i;
        setRandomPosition(entity);
        wanderEntities.push(entity);
        scene.add(entity);
      }

      // Seek entity Mesh
      let pursueEntitiesGeometry = new THREE.BoxGeometry(25, 50, 15);
      let pursueEntitiesMaterial = new THREE.MeshBasicMaterial({
        color: 0xff0000,
        wireframe: true,
      });

      let firstPursueEntityMaterial = new THREE.MeshBasicMaterial({
        color: 0x0000ff,
        wireframe: true,
      });
      // Seek entities
      pursueEntities = [];
      for (let i = 0; i < NUM_ENTITIES; i++) {
        let entityMesh;

        if (i === 0) {
          entityMesh = new THREE.Mesh(
            pursueEntitiesGeometry,
            firstPursueEntityMaterial
          );
        } else {
          entityMesh = new THREE.Mesh(
            pursueEntitiesGeometry,
            pursueEntitiesMaterial
          );
        }
        entityMesh.position.setY(25);

        let entity = new SteeringEntity(i + 10, entityMesh);
        entity.name = "seekEntity" + i;
        setRandomPosition(entity);
        pursueEntities.push(entity);
        scene.add(entity);
      }

      //Plane boundaries (do not cross)
      boundaries = new THREE.Box3(
        new THREE.Vector3(-MAP_LIMIT, 0, -MAP_LIMIT),
        new THREE.Vector3(MAP_LIMIT, 0, MAP_LIMIT)
      );

      params = {
        maxSpeed: 1,
        maxForce: 1,
        lookAtDirection: true,
        wanderDistance: 1,
        wanderRadius: 1,
        wanderRange: 2,
      };

      //Gui
      let gui = new dat.GUI();
      gui
        .add(heightMap, "heightIterator", 0, 0.5)
        .name("Height map iterator")
        .step(0.001);

      window.addEventListener("resize", onWindowResize, false);
      animate();
    }

    function onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    }

    function animate() {
      iterationsCount += 1;

      animationFrame = requestAnimationFrame(animate);
      controls.update();

      if (iterationsCount < MAX_ITERATIONS) {
        for (let i = 0; i < wanderEntities.length; i++) {
          // Wander
          wanderEntities[i].wander();

          if (params.lookAtDirection) wanderEntities[i].lookWhereGoing(true);
          else wanderEntities[i].rotation.set(0, 0, 0);

          wanderEntities[i].bounce(boundaries);
          wanderEntities[i].update();

          if (i % 2 === 0) {
            heightMap.heightIterator = MAX_HEIGHT_MAP_ITERATOR;
          } else {
            heightMap.heightIterator = MIN_HEIGHT_MAP_ITERATOR;
          }
          
          heightMap.update(wanderEntities[i]);

          // Seek
          pursueEntities[i].pursue(wanderEntities[i]);
          pursueEntities[i].maxSpeed = wanderEntities[i].maxSpeed / 2;

          if (params.lookAtDirection) pursueEntities[i].lookWhereGoing(true);
          else pursueEntities[i].rotation.set(0, 0, 0);

          pursueEntities[i].bounce(boundaries);
          pursueEntities[i].update();

          heightMap.update(pursueEntities[i]);
        }
      } else if (iterationsCount === MAX_ITERATIONS) {
        constructTerrain();
        scene.remove(floor);

        for (let i = 0; i < wanderEntities.length; i++) {
          let wanderEntity = scene.getObjectByName("wanderEntity" + i);
          scene.remove(wanderEntity);

          let seekEntity = scene.getObjectByName("seekEntity" + i);
          scene.remove(seekEntity);
        }

        downloadCsv(heightMap, "Wander_Pursue_half");
        downloadScreenshot("Wander_Pursue_half");
      }

      renderer.render(scene, camera);
      stats.update();
    }
  </script>

  <body onload="init('container')">
    <div id="container"></div>
    <!-- <div id="msg">
      <span style="color: #bfff00; font-weight: bold"
        >Press 't' to construct the terrain:
      </span>
      <br />After this will not be possible to update<br />the height map.
    </div> -->
    <a
      href="https://github.com/erosmarcon/three-steer/blob/master/examples/Arrive.html"
      target="_blank"
      ><div class="view-source">View source</div></a
    >
  </body>
</html>
